home *** CD-ROM | disk | FTP | other *** search
/ AI Game Programming Wisdom / AIGameProgrammingWisdom.iso / SourceCode / 10 Scripting / 02 Berger / sinterp / VM.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-16  |  5.6 KB  |  280 lines

  1. #include <stdio.h>
  2. #include <stdarg.h>
  3.  
  4. #include "VM.H"
  5.  
  6.  
  7.  
  8. VM::VM( const char *stream, size_t size )
  9. {
  10.   // Setup the bytecode stream data members to point to the bytecode stream
  11.   // that the interpreter is supposed to execute.
  12.   m_stream = stream;
  13.   m_streamSize = size;
  14.  
  15.   // Setup each of the opcode handlers.
  16.   m_opHandlers[Add_Opcode]      = HandleBinOp;
  17.   m_opHandlers[Subtract_Opcode] = HandleBinOp;
  18.   m_opHandlers[Multiply_Opcode] = HandleBinOp;
  19.   m_opHandlers[Divide_Opcode]   = HandleBinOp;
  20.  
  21.   m_opHandlers[Push_Opcode]     = HandlePush;
  22.   m_opHandlers[Pop_Opcode]      = HandlePop;
  23.   m_opHandlers[Dupe_Opcode]     = HandleDupe;
  24.  
  25.   m_opHandlers[Load_Opcode]     = HandleLoad;
  26.   m_opHandlers[Store_Opcode]    = HandleStore;
  27.  
  28.   m_opHandlers[Jump_Opcode]     = HandleJump;
  29.   m_opHandlers[IfZero_Opcode]   = HandleIfZero;
  30. }
  31.  
  32.  
  33. VM::~VM()
  34. {
  35. }
  36.  
  37.  
  38.  
  39. void
  40. VM::Exec()
  41. {
  42.   // Make sure that instruction pointer starts at the beginning of the
  43.   // bytecode stream.  This is needed in case the caller constructs a single
  44.   // VM class and wants to execute the same bytecode stream multiple times.
  45.   m_ip = m_stream;
  46.  
  47.   for ( ;; ) {
  48.     // Get the next opcode in the stream.  If there are no opcodes left, then
  49.     // Num_Opcode will be returned.  Break out of the loop since there is
  50.     // nothing left to do.
  51.     Opcode op = GetNextOpcode();
  52.     if ( op == Num_Opcode )
  53.       break;
  54.  
  55.     // Fetch the proper opcode handler from our function pointer table and
  56.     // pass the flow of control to the handler.  If this handler returns
  57.     // false, then something bad has happened.  A real VM will raise errors at
  58.     // this point (and maybe even drop a core?).
  59.     OpcodeHandler handler = m_opHandlers[op];
  60.  
  61.     if ( !(this->*handler)( op ) )
  62.       break;
  63.   }
  64. }
  65.  
  66.  
  67. Opcode
  68. VM::GetNextOpcode()
  69. {
  70.   // The instruction pointer always points to the next instruction to
  71.   // execute; therefore, it is possible that it points outside of the bytecode
  72.   // stream.  If this happens, then we've reached the end of the instructions
  73.   // we are supposed to execute.
  74.   const char *end = m_stream + m_streamSize;
  75.  
  76.   if ( m_ip >= end )
  77.     return Num_Opcode;
  78.  
  79.   // Fetch the next opcode from the bytecode stream and increment the
  80.   // instruction pointer.
  81.   Opcode op = *(Opcode *)m_ip;
  82.   m_ip += sizeof( Opcode );
  83.  
  84.   return op;
  85. }
  86.  
  87.  
  88. int
  89. VM::GetOpcodeArg()
  90. {
  91.   // Fetch the opcode's argument from the bytecode stream and increment the
  92.   // instruction pointer.
  93.   int arg = *(int *)m_ip;
  94.   m_ip += sizeof( int );
  95.  
  96.   return arg;
  97. }
  98.  
  99.  
  100. bool
  101. VM::HandleBinOp( Opcode op )
  102. {
  103.   char *type;
  104.   int result;
  105.  
  106.   // All binary operations have two arguments; therefore, pop off the left and
  107.   // right hands sides of the operation.  Note that the order here is
  108.   // important, and this is the same order that the code generator will push
  109.   // the values onto the stack.
  110.   int rhs = Pop();
  111.   int lhs = Pop();
  112.  
  113.  
  114.   // Ensure the script doesn't try to divide by zero!
  115.   if ( op == Divide_Opcode && rhs == 0 ) {
  116.     printf( "division by zero.\n" );
  117.     return false;
  118.   }
  119.  
  120.  
  121.   switch ( op ) {
  122.     case Add_Opcode:        type = "+";   result = lhs + rhs;   break;
  123.     case Subtract_Opcode:   type = "-";   result = lhs - rhs;   break;
  124.     case Multiply_Opcode:   type = "*";   result = lhs * rhs;   break;
  125.     case Divide_Opcode:     type = "/";   result = lhs / rhs;   break;
  126.   }
  127.  
  128.  
  129.   Push( result );
  130.  
  131.   Trace( "%d %s %d", lhs, type, rhs );
  132.  
  133.   return true;
  134. }
  135.  
  136.  
  137. bool
  138. VM::HandlePush( Opcode op )
  139. {
  140.   int arg = GetOpcodeArg();
  141.  
  142.   Push( arg );
  143.  
  144.   Trace( "pushing %d", arg );
  145.  
  146.   return true;
  147. }
  148.  
  149.  
  150. bool
  151. VM::HandlePop( Opcode op )
  152. {
  153.   int val = Pop();
  154.  
  155.   Trace( "pop %d", val );
  156.  
  157.   // In this sample compiler, pop will be generated at the end of a statement.
  158.   // This blank line makes it easier to match up statements with a script.
  159.   printf( "\n" );
  160.  
  161.   return true;
  162. }
  163.  
  164.  
  165. bool
  166. VM::HandleDupe( Opcode op )
  167. {
  168.   int val = Pop();
  169.  
  170.   Push( val );
  171.   Push( val );
  172.  
  173.   Trace( "duping %d", val );
  174.  
  175.   return true;
  176. }
  177.  
  178.  
  179. bool
  180. VM::HandleLoad( Opcode op )
  181. {
  182.   int arg = GetOpcodeArg();
  183.  
  184.   Push( m_stack[arg] );
  185.  
  186.   Trace( "loading %d", arg );
  187.  
  188.   return true;
  189. }
  190.  
  191.  
  192. bool
  193. VM::HandleStore( Opcode op )
  194. {
  195.   int arg = GetOpcodeArg();
  196.   int val = Pop();
  197.  
  198.   m_stack[arg] = val;
  199.  
  200.   Trace( "storing %d at %d", val, arg );
  201.  
  202.   return true;
  203. }
  204.  
  205.  
  206. bool
  207. VM::HandleJump( Opcode op )
  208. {
  209.   int arg = GetOpcodeArg();
  210.  
  211.   // Since this jump opcode is a relative jump, add the opcode's argument to
  212.   // the instruction pointer.
  213.   m_ip += arg;
  214.  
  215.   Trace( "jumping %d", arg );
  216.  
  217.   return true;
  218. }
  219.  
  220.  
  221. bool
  222. VM::HandleIfZero( Opcode op )
  223. {
  224.   int arg = GetOpcodeArg();
  225.   int val = Pop();
  226.  
  227.   // If the value on the stack is zero, then increment the instruction pointer
  228.   // by the specified amount.x
  229.   if ( val == 0 )
  230.     m_ip += arg;
  231.  
  232.   Trace( "if %d == 0 then %d", val, arg );
  233.  
  234.   return true;
  235. }
  236.  
  237.  
  238. void
  239. VM::Trace( char *fmt, ... )
  240. {
  241.   va_list args;
  242.  
  243.   char buf[128];
  244.   char out[128];
  245.   size_t pos;
  246.  
  247.   // Build up a buffer with the opcode's spew.
  248.   va_start( args, fmt );
  249.   vsprintf( buf, fmt, args );
  250.   va_end( args );
  251.  
  252.   // Iterate through the stack and dump its contents.
  253.   pos = sprintf( out, "%-25s -=-  ", buf );
  254.  
  255.   RuntimeStack::iterator i = m_stack.begin();
  256.   RuntimeStack::iterator end = m_stack.end();
  257.  
  258.   for ( ; i != end; ++i )
  259.     pos += sprintf( &out[pos], " %2d", *i );
  260.  
  261.   puts( out );
  262. }
  263.  
  264.  
  265. void
  266. VM::Push( int arg )
  267. {
  268.   m_stack.push_back( arg );
  269. }
  270.  
  271.  
  272. int
  273. VM::Pop()
  274. {
  275.   int arg = m_stack.back();
  276.   m_stack.pop_back();
  277.  
  278.   return arg;
  279. }
  280.